package de.sane.math

case class Rational(private val n: Long, private val d: Long) {

	def this(n: Long) = this(n, 1)
	
	require(d != 0)
	
	val numer: Long = if(d < 0) -n else n
	val denom: Long = if(d < 0) -d else d	
		
	def gcd(a: Long, b: Long): Long = {
		if(b == 0) a else gcd(b, a % b)
	}
	
	def cancel(): Rational = {
		val g = gcd(numer, denom)
		new Rational(numer / g, denom / g)
	}
	
	def +(that: Rational): Rational = {
		new Rational(this.numer * that.denom + that.numer * this.denom, this.denom * that.denom)
	}
	
	def -(that: Rational): Rational = {
		new Rational(this.numer * that.denom - that.numer * this.denom, this.denom * that.denom)
	}
	
	def *(that: Rational): Rational = {
		new Rational(this.numer * that.numer, this.denom * that.denom)
	}
	
	def /(that: Rational): Rational = {
		new Rational(this.numer * that.denom, this.denom * that.numer)
	}
	
	def exp(exponent: Long): Rational = {
		require(exponent >= 0)
		if(exponent == 0) Rational(1) else this * exp(exponent - 1)
	}
	
	def <(that: Rational): Boolean = {
		this.numer * that.denom < that.numer * this.denom
	}
	
	def >(that: Rational): Boolean = {
		this.numer * that.denom > that.numer * this.denom
	}
		
	override def equals(other: Any): Boolean = {
		other match {
			case that: Rational => {
				val gcdThis = gcd(numer, denom)
				val gcdThat = gcd(that.numer, that.denom)
				this.numer / gcdThis == that.numer / gcdThat && this.denom / gcdThis == that.denom / gcdThat
			}
			case _ => false
		}
	}
		
	override def toString(): String = {
		"" + numer + "/" + denom
	}
}

object Rational {

	def apply(value: Long) = new Rational(value, 1)
	
	implicit def longToRational(value: Long) = new Rational(value, 1)

}

